package com.liaoyifan.core.aspect;

import com.liaoyifan.core.annotation.PreventDuplicateSubmit;
import com.liaoyifan.core.constant.Constants;
import com.liaoyifan.core.enums.CoreCodeEnum;
import com.liaoyifan.core.exception.ResultException;
import com.liaoyifan.core.util.Redis;
import com.liaoyifan.core.util.SpELKey;
import java.time.Duration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;

@SuppressWarnings("unused")
@Aspect
@Order(3)
public class PreventDuplicateSubmitAspect {

    private static final Logger log =
            LogManager.getLogger(PreventDuplicateSubmitAspect.class.getSimpleName());

    @Around(
            "@annotation(com.liaoyifan.core.annotation.PreventDuplicateSubmit) || @within(com.liaoyifan.core.annotation.PreventDuplicateSubmit)")
    public Object preventDuplicateSubmitAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        PreventDuplicateSubmit annotation =
                signature.getMethod().getAnnotation(PreventDuplicateSubmit.class);
        if (annotation == null) {
            annotation =
                    AnnotationUtils.findAnnotation(
                            joinPoint.getTarget().getClass(), PreventDuplicateSubmit.class);
        }
        String key = SpELKey.preventDuplicateSubmitKey(signature, joinPoint.getArgs());
        log.info("PreventDuplicateSubmitKey: {}", key);
        assert annotation != null;
        if (!Redis.setIfAbsent(key, Constants.ONE, Duration.ofSeconds(annotation.expire()))) {
            throw new ResultException(CoreCodeEnum.REPEAT_REQUEST);
        }
        try {
            return joinPoint.proceed();
        } finally {
            Redis.delete(key);
        }
    }
}
